/* Name: main.c
 * Author: <insert your name here>
 * Copyright: <insert your copyright message here>
 * License: <insert your license reference here>
 */
#include "uart.h"
#include "usbdrv.h"
#include "oddebug.h"
#include "hardware.h"
#include "serial.h"
#include "wireless.h"
#include "ds18b20.h"
#include "ircode.h"

#include <stdio.h>
#include <string.h>

#define RF_ERROR_SYNC	0xEE
#define RF_SETUP_SYNC	0xFC
#define RF_WRITE_SYNC	0xFD
#define RF_READ_SYNC	0xFE
#define RF_WRITE_IDLE   0xCC
#define RF_WRITE_OK		0x00
#define RF_WRITE_ERR	0xFF

// u2w device request
typedef enum {
	U2WRQ_RADIO_WRITE = 0,	// write data to radio
	U2WRQ_RADIO_READ,		// read data from radio buffer
	U2WRQ_RADIO_RCOUNT,		// data count from radio read buffer
	U2WRQ_RADIO_REGRW,		// radio chip register read/write
	U2WRQ_RADIO_REGBIT,
	U2WRQ_MEM8_RW,
	U2WRQ_MEM8_BIT,
	U2WRQ_MCU_RESET,
	U2WRQ_FLASH_WRITE,
	U2WRQ_FLASH_READ,
	U2WRQ_EEPROM_WRITE,
	U2WRQ_EEPROM_READ,
	U2WRQ_IR_CONFIG,
	U2WRQ_IR_WDATA,
	U2WRQ_LAST_ERR,
	U2WRQ_READ_TEMP,
	U2WRQ_IDLE,
} u2w_request_t;

// usb state
typedef enum {
	USB_STATE_READ_RADIO,
	USB_STATE_WRITE_RADIO,
	USB_STATE_IR_CONFIG,
	USB_STATE_IR_WDATA,
	USB_STATE_WRITE_EEPROM,
	USB_STATE_READ_EEPROM,
	USB_STATE_WRITE_FLASH,
	USB_STATE_READ_FLASH,
	USB_STATE_READ_TEMP,
	USB_STATE_MCU_RESET,
	USB_STATE_IDLE,
} usb_state_t;

// prx package type
typedef struct {
	uint8_t sync;
	uint8_t argument;
} prx_package_t;

// usb message and state
typedef struct {
	usb_state_t state;
	uchar result;
	int wValue;
	int wIndex;
	int dataLength;
	int dataAddress;
	int asyncJobs;
	int asyncLastError;
	char temperature[2];
	char dataBuffer[64];
} usb_msg_t;

// uart data stream process state
typedef enum {
	UART_STATE_READ_SYNC,
	UART_STATE_READ_AUGU,
	UART_STATE_READ_DATA,
	UART_STATE_PROCESS_PKG,
} uart_state_t;

// uart message and state
typedef struct {
	uart_state_t state;
	char input[32];
} uart_msg_t;

static usb_msg_t usbMessage = {USB_STATE_IDLE, 0};

static void bluetoothDataProcess(void);
static void systemAsyncJobs(void);
static void rfRxEndHandler(char *buffer, int length);
static void rfTxEndHandler(int err);

//static usb_state_t usbState = USB_STATE_IDLE;
//static int usbDataLength = 0;
//static int usbDataBufferIndex = 0;
//static int irDataLen = 0;
//static uchar usbDataBuffer[48];
//static uchar usbCtrlReqRet = 0;
//static int irDataReady = 0;

//void systemBasicIOTest() {
//	int i;
//	
//	_CONFIG_IO_OUTPUT(LED_PORT, LED_PIN);
//	
//	for (i = 0; i < 10; i ++) {
//		_CLR_IO(LED_PORT, LED_PIN);
//		_delay_ms(500);
//		_SET_IO(LED_PORT, LED_PIN);
//		_delay_ms(500);
//	}
//}

int main(void)	{
	uchar i = 0;
	//uchar count = 0;
	char serialNumber[6];
	//char testData[32] = {0};
	sys_role_t rfRole;
	
	// systemBasicIOTest();
	uart1_open(115200);
	
	while (1)
		kprintf("test\r\n");
	
	// timer2 as temperature period read
	TCCR2 = (7 << 0);
	TIMSK = (1 << 6);
	
	readSerialNumber(serialNumber);
	rfRole = getRFSystemRole();
	
	// serial number [0] = rfChannel
	// serial number [1..5] = rfAddress
	wirelessInit(serialNumber[0], rfRole, RF_DR_250K, RF_PW_MAX, &serialNumber[1]);
	setTxEndCallback(rfTxEndHandler);
	setRxEndCallback(rfRxEndHandler);
//	tempMain();
//	IRMain();
	
	// PRX device will stop here forever
	if (rfRole == SYS_ROLE_PRX)
	{
		// test LED pins
		io_output_high(BLRST_PORT, BLRST_PIN);
		
		sei();
		wdt_enable(WDTO_2S);
		while (1)
		{
			wdt_reset();
			systemAsyncJobs();
		}
	}
	
//	sei();
//	while (1) {
//		// serialNumber[0] = count;
//		DBG("write RF package index = %d", count ++);
//		for (i = 0; i < sizeof(testData); i ++)
//		{
//			testData[i] = count & 0xFF;
//		}
//		
//		wirelessWriteSync(serialNumber, testData, sizeof(testData));
//		_delay_ms(1000);
//	}
	
	odDebugInit();
    DBG1(0x00, 0, 0);       /* debug output: main starts */
    usbInit();
    usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
	
	wdt_enable(WDTO_2S);
	
	i = 0;
    while(-- i) {            /* fake USB disconnect for > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
	
    usbDeviceConnect();
    sei();
    DBG1(0x01, 0, 0);       /* debug output: main loop starts */
	
	io_output_low(LED_PORT, LED_PIN);
	
    for(;;){                /* main event loop */
        DBG1(0x02, 0, 0);   /* debug output: main loop iterates */
        wdt_reset();
        usbPoll();
		
		//bluetoothDataProcess();
		systemAsyncJobs();
    }
    return 0;
}

// asynchronous processing job which will take long time to finish
// that can't be done during rf or usb package
static void systemAsyncJobs(void)
{
	if(usbMessage.asyncJobs)
	{
		usbMessage.asyncJobs -= 1;
		
		if (usbMessage.asyncJobs == 0)
		{
			// usbMessage.asyncLastError = RF_WRITE_OK;
			if(usbMessage.state == USB_STATE_IR_WDATA)
			{
				// DBG("IR code!");
				IRUSBWrite((const char *)usbMessage.dataBuffer, usbMessage.dataLength);
			}
			else if(usbMessage.state == USB_STATE_MCU_RESET)
			{
				wdt_enable(WDTO_15MS);
				while (1);
			}
			else if(usbMessage.state == USB_STATE_WRITE_EEPROM)
			{
				DBG("write eeprom %d, %d", usbMessage.dataAddress, usbMessage.dataLength);
				if (eepromWrite(usbMessage.dataBuffer, usbMessage.dataAddress, usbMessage.dataLength) != 0)
				{
					usbMessage.asyncLastError = RF_WRITE_ERR;
				}
				else
				{
					usbMessage.asyncLastError = RF_WRITE_OK;
				}
			}
			else if(usbMessage.state == USB_STATE_WRITE_FLASH)
			{
				//if (flashWrite(usbMessage.dataBuffer, usbMessage.dataAddress, usbMessage.dataLength) != 0)
				//{
				//	usbMessage.asyncLastError = RF_WRITE_ERR;
				//}
			}
		}
	}
}

usbMsgLen_t usbFunctionSetup(uchar data[8])	{
	usbRequest_t *rq = (void *)data;
	u2w_request_t uReq;
	
//	int i;
//	
//	kprintf("upkg: ");
//	for(i = 0; i < 8; i ++)
//	{
//		kprintf("%02x ", data[i]);
//	}
//	kprintf("\r\n");
	
	if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_VENDOR){
		usbMsgPtr = (usbMsgPtr_t)&usbMessage.result; // &usbCtrlReqRet;
		usbMessage.wValue = rq->wValue.word;
		usbMessage.wIndex = rq->wIndex.word;
		
		uReq = (u2w_request_t)rq->bRequest & 0x7F;
		
		// DBG("bReq=0x%x", uReq);
		
		if (uReq == U2WRQ_RADIO_WRITE)
		{
			//
			// write RF package with channel and address
			// data length = usb request: wValue
			// data[0..5] = serialNumber
			// data[0] = RF-Channel, data[1..5] = PRX-Address
			// data[6..N] = RF data
			//
			usbMessage.state = USB_STATE_WRITE_RADIO;
			usbMessage.dataLength = rq->wValue.word;
			return USB_NO_MSG;
		}
		else if (uReq == U2WRQ_RADIO_READ)
		{
			//
			// read RF cbuffer cache
			// data length = usb request: wValue
			//
			usbMessage.state = USB_STATE_READ_RADIO;
			usbMessage.dataLength = rq->wValue.word;
			return USB_NO_MSG;
		}
		else if (uReq == U2WRQ_LAST_ERR)
		{
			//
			// read radio last tranmit error code
			//
			usbMessage.result = usbMessage.asyncLastError;
			// DBG("%x", usbMessage.result);
			return 1;
		}
		else if (uReq == U2WRQ_RADIO_RCOUNT)
		{
			//
			// read number of bytes in cbuffer cache
			//
			usbMessage.result = wirelessReadCount();
			return 1;
		}
		else if (uReq == U2WRQ_RADIO_REGRW)
		{
			//
			// read/write radio chip register
			// usb bRequest bit 7: 1 = write, 0 = read
			//
			if (rq->bRequest & 0x80) {
				wirelessWriteReg(rq->wIndex.word, rq->wValue.word);
			}
			usbMessage.result = wirelessReadReg(rq->wIndex.word);
			return 1;
		}
		else if (uReq == U2WRQ_RADIO_REGBIT)
		{
			//
			// set/clear radio chip register
			// usb bRequest bit 7: 1 = set, 0 = clear
			// usb wIndex = address
			// usb wValue = data mask
			//
			usbMessage.result = wirelessReadReg(rq->wIndex.word);
			if (rq->bRequest & 0x80)
			{
				usbMessage.result |= rq->wValue.word;
			}
			else
			{
				usbMessage.result &= ~rq->wValue.word;
			}
			wirelessWriteReg(rq->wIndex.word, usbMessage.result);
			usbMessage.result = wirelessReadReg(rq->wIndex.word);
			return 1;
		}
		else if (uReq == U2WRQ_MCU_RESET)
		{
			//
			// reset avr device
			//
			if ( (rq->wIndex.word == 0x1985) && (rq->wValue.word == 0x0709) )
			{
				// wdt_enable(WDTO_15MS);
				// while (1);
				usbMessage.state = USB_STATE_MCU_RESET;
				usbMessage.asyncJobs = 38;
			}
		}
		else if (uReq == U2WRQ_FLASH_WRITE)
		{
			//
			// write flash data
			// usb wIndex = address
			// usb wValue = number of bytes to write
			//
			usbMessage.state = USB_STATE_WRITE_FLASH;
			usbMessage.dataLength = rq->wValue.word;
			usbMessage.dataAddress = rq->wIndex.word;
			return USB_NO_MSG;
		}
		else if (uReq ==U2WRQ_FLASH_READ)
		{
			//
			// read flash data
			// usb wIndex = address
			// usb wValue = number of bytes to write
			//
			usbMessage.state = USB_STATE_READ_FLASH;
			usbMessage.dataLength = rq->wValue.word;
			usbMessage.dataAddress = rq->wIndex.word;
			return USB_NO_MSG;
		}
		else if (uReq ==U2WRQ_EEPROM_WRITE)
		{
			//
			// write eeprom data
			// usb wIndex = address
			// usb wValue = number of bytes to write
			//
			usbMessage.state = USB_STATE_WRITE_EEPROM;
			usbMessage.dataLength = rq->wValue.word;
			usbMessage.dataAddress = rq->wIndex.word;
			return USB_NO_MSG;
		}
		else if (uReq ==U2WRQ_EEPROM_READ)
		{
			//
			// read eeprom data
			// usb wIndex = address
			// usb wValue = number of bytes to write
			//
			usbMessage.state = USB_STATE_READ_EEPROM;
			usbMessage.dataLength = rq->wValue.word;
			usbMessage.dataAddress = rq->wIndex.word;
			return USB_NO_MSG;
		}
		else if (uReq == U2WRQ_MEM8_RW)
		{
			//
			// read/write avr register
			// usb bRequest bit 7: 1 = write, 0 = read
			// usb wIndex.bytes[0] = address
			// usb wValue.bytes[0] = value
			//
			if (rq->bRequest & 0x80)
			{
				_SFR_MEM8(rq->wIndex.bytes[0]) = rq->wValue.bytes[0];
			}
			usbMessage.result = _SFR_MEM8(rq->wIndex.bytes[0]);
			// DBG("result: 0x%x", usbMessage.result);
			return 1;
		}
		else if (uReq == U2WRQ_MEM8_BIT)
		{
			//
			// set/clear avr register
			// usb bRequest bit 7: 1 = set, 0 = clear
			// usb wIndex.bytes[0] = address
			// usb wValue.bytes[0] = data mask
			//
			if (rq->bRequest & 0x80)
			{
				_SFR_MEM8(rq->wIndex.bytes[0]) |= rq->wValue.bytes[0];
			}
			else
			{
				_SFR_MEM8(rq->wIndex.bytes[0]) &= ~(rq->wValue.bytes[0]);
			}
			usbMessage.result = _SFR_MEM8(rq->wIndex.bytes[0]);
			return 1;
		}
		else if (uReq == U2WRQ_IR_CONFIG)
		{
			//
			// write IR config data
			// usb wValue = data length
			//
			usbMessage.state = USB_STATE_IR_CONFIG;
			usbMessage.dataLength = rq->wValue.word;
			return USB_NO_MSG;
		}
		else if (uReq == U2WRQ_IR_WDATA)
		{
			//
			// write IR data
			// usb wValue = data length
			// data[0] = total bits of IR code
			// data[1] = blank time after IR code in ms
			// data[2..N] = IR data
			//
			usbMessage.state = USB_STATE_IR_WDATA;
			usbMessage.dataLength = rq->wValue.word;
			// DBG("wir %d", usbMessage.dataLength);
			return USB_NO_MSG;
		}
		else if (uReq == U2WRQ_READ_TEMP)
		{
			//
			// read temperature
			// fixed transfer length 2 bytes
			//
			usbMessage.state = USB_STATE_READ_TEMP;
			usbMessage.dataLength = 2;
			return USB_NO_MSG;
		}
		else if (uReq == U2WRQ_IDLE)
		{
			//
			// do nothing for idle command
			//
			return USB_NO_MSG;
		}
	}
	return 0;
}

#if (USB_CFG_IMPLEMENT_FN_READ)
uchar usbFunctionRead(uchar *data, uchar len) {
	int ret = len;
	
	// DBG("read");
	if(usbMessage.state == USB_STATE_READ_RADIO)
	{
		ret = wirelessRead((char *)data, (int)len);
		// DBG("n=%d", ret);
		return ret;
	}
	else if(usbMessage.state == USB_STATE_READ_EEPROM)
	{
		// these should be fast enough to finish in RF or USB transmit
		ret = eepromRead((char *)data, usbMessage.dataAddress, len);
		usbMessage.dataAddress += len;
		return ret;
	}
	else if(usbMessage.state == USB_STATE_READ_FLASH)
	{
		// ret = flashRead(data, usbMessage.dataAddress, len);
		usbMessage.dataAddress += len;
		return ret;
	}
	else if(usbMessage.state == USB_STATE_READ_TEMP)
	{
		data[1] = usbMessage.temperature[0];
		data[0] = usbMessage.temperature[1];
		return len;
	}
	return 0;
}
#endif

/* ------------------------------------------------------------------------- */
#if (USB_CFG_IMPLEMENT_FN_WRITE)
uchar   usbFunctionWrite(uchar *data, uchar len) {
	static int index = 0;
	
//	if( (index + len) >= sizeof(usbMessage.dataBuffer))
//	{
//		len = sizeof(usbMessage.dataBuffer) - index - 1;
//		
//		if (len == 0)
//		{
//			return 0;
//		}
//	}
	
	memcpy(&usbMessage.dataBuffer[index], data, len);
	
	index += len;
	
	if (index >= usbMessage.dataLength) {
		index = 0;
		
		if ( (usbMessage.state == USB_STATE_IR_WDATA)	  ||
			 (usbMessage.state == USB_STATE_WRITE_EEPROM) ||
			 (usbMessage.state == USB_STATE_WRITE_FLASH) )
		{
			// this will buy some time for async jobs
			usbMessage.asyncJobs = 38;
			usbMessage.asyncLastError = RF_WRITE_IDLE;
		}
		else if (usbMessage.state == USB_STATE_IR_CONFIG)
		{
			if(IRCodingConfig((const char *)usbMessage.dataBuffer, usbMessage.wIndex) != 0);
				return 0;
		}
		else if (usbMessage.state == USB_STATE_WRITE_RADIO)
		{
			// DBG("write radio %d", usbMessage.dataLength - 6);
			usbMessage.asyncLastError = RF_WRITE_IDLE;
			wirelessWriteAsync((const char *)&usbMessage.dataBuffer[0], (const char *)&usbMessage.dataBuffer[6],
							   usbMessage.dataLength - 6);
		}
	}
	return len;
}
#endif


//
// PRX device handle the RF package
// THIS FUNCTION WILL BE CALLED ONLY IN PRX MODE
// to handle command which hava more the 32 bytes argument, we make the RF package as usb package
// data[0] = header: RF_SETUP_SYNC(USB setup), RF_READ_SYNC(USB read), RF_WRITE_SYNC(USB write)
// data[1] = argument
// data[2..n] = data(opt)
// if package need any data back, they will be in RF ACK-Payload, write another package to get it back
// for RF_WRITE_SYNC package, the return data should be RF_WRITE_ERR or RF_WRITE_OK
//
void prxRFPackageHandler(const char *buffer, int length, char *rfACKBuffer, int *rfACKLength) {
	usbRequest_t *rq;
	prx_package_t *pkg = (prx_package_t *)buffer;
	int ret;
	
	*rfACKLength = 0;
	
	// this is a setup package
	if (pkg->sync == RF_SETUP_SYNC)
	{
		//DBG("setup!");
		// the usb setup package always 8 bytes length
		if ( (pkg->argument != 8) && (length != 10) )
		{
			// DBG("pkg length error");
			return;
		}
		rq = (void *)&buffer[2];
		
		// all U2WRQ_RADIO commands are disable for PRX device
		if ((u2w_request_t)(rq->bRequest & 0x7F) <= U2WRQ_RADIO_REGBIT)
		{
			// DBG("radio forbidden");
			return;
		}
		ret = (int)usbFunctionSetup((uchar *)rq);
		
		if (ret == USB_NO_MSG)
		{
			*rfACKLength = 0;
		}
		else
		{
			*rfACKLength = ret;
		}
		
		if(*rfACKLength)
		{
			// usb return data is in usbMsgPtr
			memcpy(rfACKBuffer, (char *)usbMsgPtr, *rfACKLength);
		}
	}
	else if (pkg->sync == RF_READ_SYNC)
	{
		//DBG("read!");
		// read data is in RF-ACK payload FIFO
		// write another RF package to get it back
		*rfACKLength = usbFunctionRead((uchar *)rfACKBuffer, pkg->argument);
	}
	else if (pkg->sync == RF_WRITE_SYNC)
	{
		//DBG("write!");
		// dataLength should be (length - 2) for WRITE package
		if ((length - 2) == pkg->argument)
		{
			// write result is in usbMessage.asyncLastError
			usbFunctionWrite((uchar *)&buffer[2], pkg->argument);
		}
	}
}

//
// commands comes form bluetooth with UART channel
// simulate as RF package
//
static void bluetoothDataProcess() {
	static uart_msg_t msg = { UART_STATE_READ_SYNC, {0} };
	static int remainingBytes = 0;
	static int dataIndex = 0;
	char output[32];
	int ret, i;
	
	// read sync byte
	while (msg.state == UART_STATE_READ_SYNC)
	{
		if (uartRead((char *)&msg.input[0], 1))
		{
			if ( (msg.input[0] == RF_SETUP_SYNC) ||
				 (msg.input[0] == RF_WRITE_SYNC) ||
				 (msg.input[0] == RF_READ_SYNC) )
			{
				msg.state = UART_STATE_READ_AUGU;
			}
		}
		else
		{
			break;
		}
	}
	
	// read argument byte
	while (msg.state == UART_STATE_READ_AUGU)
	{
		if (uartRead((char *)&msg.input[1], 1))
		{
			if (msg.input[0] == RF_READ_SYNC)
			{
				msg.state = UART_STATE_PROCESS_PKG;
			}
			else
			{
				msg.state = UART_STATE_READ_DATA;
				remainingBytes = msg.input[1];
			}
			dataIndex = 2;
		}
		else
		{
			break;
		}
	}
	
	// read package data
	while (msg.state == UART_STATE_READ_DATA)
	{
		ret = uartRead(&msg.input[dataIndex], remainingBytes);
			
		if (ret)
		{
			dataIndex += ret;
			remainingBytes -= ret;
			
			if (remainingBytes == 0)
			{
				msg.state = UART_STATE_PROCESS_PKG;
			}
		}
		else
		{
			break;
		}
	}
	
	// package ready! process it
	if(msg.state == UART_STATE_PROCESS_PKG)
	{
		prxRFPackageHandler(msg.input, dataIndex, output, &ret);
		
		if(ret)
		{
			for(i = 0; i < ret; i ++)
			{
				uart1_putc(output[i]);
			}
		}
		
		msg.state = UART_STATE_READ_SYNC;
	}
}

static void rfTxEndHandler(int err) {
	// DBG("txend, %d", err);
	if (err)
	{
		usbMessage.asyncLastError = RF_WRITE_ERR;
	}
	else
	{
		usbMessage.asyncLastError = RF_WRITE_OK;
	}
}

static void rfRxEndHandler(char *buffer, int length)
{
	char payload[32];
	int payloadLength = 0;
	
//	int i;
	
//	kprintf("rcv %d bytes: ", length);
//	for(i = 0; i < length; i ++) {
//		kprintf("%02x ", buffer[i] & 0xFF);
//	}
//	kprintf("\r\n");
	
	prxRFPackageHandler(buffer, length, payload, &payloadLength);
	
	if (payloadLength)
	{
		//DBG("0x%x", payload[0]);
		writePRXAckPayload(payload, payloadLength);
	}
}

// every 16.7ms
ISR(TIMER2_OVF_vect) {
	static int count = 0;
	char buffer[2];
	
	count ++;
	
	// every 5s
	if (count >= 300)
	{
		count = 0;
		// DBG("temp!");
		if (readTemp(buffer) == 0)
		{
			usbMessage.temperature[1] = buffer[0];
			usbMessage.temperature[0] = buffer[1];
		}
	}
}




